/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * RSCollada.cpp
 *
 *  Created on: 2010/02/16
 *      Author: Alexandre Pires ( c.alexandre.pires@gmail.com )
 */

#include "RSDocument.h"
#include "RSDataSource.h"
#include "RSGeometriesLibrary.h"
#include "RSVisualScenesLibrary.h"
#include "RSCamerasLibrary.h"
#include "RSScene.h"

namespace Realsphere {

    RSDocument::RSDocument ( const QString& xmlns ) :
        RSElement ( RS_COLLADA_TAG )
    {
        mXmlNS = xmlns;
        mScene = NULL;
    }

    RSDocument::~RSDocument ()
    {
        mGeometriesLibraries.clear ();
        mScene = NULL;
    }

    QString RSDocument::XmlNS () const
    {
        return mXmlNS;
    }

    QString RSDocument::Base () const
    {
        return mBase;
    }

    RSScene* RSDocument::Scene () const
    {
        return mScene;
    }

    RSDataSource* RSDocument::GetDataSource ( const QString& rId )
    {
        RSDataSources data_sources = FindChildsElements<RSDataSource*> ();
        RSDataSourcesIterator iterator = RSDataSourcesIterator ( data_sources );

        while ( iterator.hasNext () )
        {
            RSDataSource* uid_element = iterator.next ();
            QString uid_element_id = uid_element->Id ();

            if ( uid_element_id == rId )
                return uid_element;
        }

        return NULL;
    }

    RSGeometriesLibrary* RSDocument::GetGeometriesLibrary ( unsigned int rIndex ) const
    {
        return mGeometriesLibraries[rIndex];
    }

    RSGeometriesLibrary* RSDocument::GetGeometriesLibrary ( const QString& rId ) const
    {
        RSGeometriesLibrariesIterator iterator = GeometriesLibrariesIterator ();

        while ( iterator.hasNext () )
        {
            RSGeometriesLibrary* current_library = iterator.next ();
            QString library_id = current_library->Id ();

            if ( library_id == rId )
                return current_library;
        }

        return NULL;
    }

    int RSDocument::IndexOf ( RSGeometriesLibrary* rGeometriesLibrary )
    {
        return mGeometriesLibraries.indexOf ( rGeometriesLibrary );
    }

    unsigned int RSDocument::GeometriesLibrariesSize ()
    {
        return mGeometriesLibraries.size ();
    }

    RSGeometriesLibraries RSDocument::GeometriesLibraries () const
    {
        return mGeometriesLibraries;
    }

    RSGeometriesLibrariesIterator RSDocument::GeometriesLibrariesIterator () const
    {
        return RSGeometriesLibrariesIterator ( mGeometriesLibraries );
    }

    RSVisualScenesLibrary* RSDocument::GetVisualScenesLibrary ( unsigned int rIndex ) const
    {
        return mVisualScenesLibraries[rIndex];
    }

    RSVisualScenesLibrary* RSDocument::GetVisualScenesLibrary ( const QString& rId ) const
    {
        RSVisualScenesLibrariesIterator iterator = VisualScenesLibrariesIterator ();

        while ( iterator.hasNext () )
        {
            RSVisualScenesLibrary* current_library = iterator.next ();
            QString library_id = current_library->Id ();

            if ( library_id == rId )
                return current_library;
        }

        return NULL;
    }

    int RSDocument::IndexOf ( RSVisualScenesLibrary* rVisualScenesLibrary )
    {
        return mVisualScenesLibraries.indexOf ( rVisualScenesLibrary );
    }

    unsigned int RSDocument::VisualScenesLibrariesSize ()
    {
        return mVisualScenesLibraries.size ();
    }

    RSVisualScenesLibraries RSDocument::VisualScenesLibraries () const
    {
        return mVisualScenesLibraries;
    }

    RSVisualScenesLibrariesIterator RSDocument::VisualScenesLibrariesIterator () const
    {
        return RSVisualScenesLibrariesIterator ( mVisualScenesLibraries );
    }

    RSCamerasLibrary* RSDocument::GetCamerasLibrary ( unsigned int rIndex ) const
    {
        return mCamerasLibraries[rIndex];
    }

    RSCamerasLibrary* RSDocument::GetCamerasLibrary ( const QString& rId ) const
    {
        RSCamerasLibrariesIterator iterator = CamerasLibrariesIterator ();

        while ( iterator.hasNext () )
        {
            RSCamerasLibrary* current_library = iterator.next ();
            QString library_id = current_library->Id ();

            if ( library_id == rId )
                return current_library;
        }

        return NULL;
    }

    int RSDocument::IndexOf ( RSCamerasLibrary* rCamerasLibrary )
    {
        return mCamerasLibraries.indexOf ( rCamerasLibrary );
    }

    unsigned int RSDocument::CamerasLibrariesSize ()
    {
        return mCamerasLibraries.size ();
    }

    RSCamerasLibraries RSDocument::CamerasLibraries () const
    {
        return mCamerasLibraries;
    }

    RSCamerasLibrariesIterator RSDocument::CamerasLibrariesIterator () const
    {
        return RSCamerasLibrariesIterator ( mCamerasLibraries );
    }

    bool RSDocument::IsRootElement () const
    {
        return true;
    }

    void RSDocument::SetXmlNS ( const QString& rXmlNS )
    {
        mXmlNS = rXmlNS;
        emit XmlNSChanged ( rXmlNS );
    }

    void RSDocument::SetBase ( const QString& rBase )
    {
        mBase = rBase;
        emit BaseChanged ( rBase );
    }

    RSError RSDocument::AddGeometriesLibrary ( RSGeometriesLibrary *rLibrary )
    {
        RSError error = AddChildElement ( rLibrary );
        return error;
    }

    RSError RSDocument::RemoveGeometriesLibrary ( RSGeometriesLibrary *rLibrary )
    {
        RSError error = RemoveChildElement ( rLibrary );
        return error;
    }

    RSError RSDocument::RemoveGeometriesLibrary ( const QString& rId )
    {
        RSGeometriesLibrary* library = GetGeometriesLibrary ( rId );
        RSError error = RemoveGeometriesLibrary ( library );
        return error;
    }

    RSError RSDocument::RemoveGeometriesLibrary ( unsigned int rIndex )
    {
        RSGeometriesLibrary* library = GetGeometriesLibrary ( rIndex );
        RSError error = RemoveGeometriesLibrary ( library );
        return error;
    }

    RSError RSDocument::AddVisualScenesLibrary ( RSVisualScenesLibrary *rLibrary )
    {
        RSError error = AddChildElement ( rLibrary );
        return error;
    }

    RSError RSDocument::RemoveVisualScenesLibrary ( RSVisualScenesLibrary *rLibrary )
    {
        RSError error = RemoveChildElement ( rLibrary );
        return error;
    }

    RSError RSDocument::RemoveVisualScenesLibrary ( const QString& rId )
    {
        RSVisualScenesLibrary* library = GetVisualScenesLibrary ( rId );
        RSError error = RemoveVisualScenesLibrary ( library );
        return error;
    }

    RSError RSDocument::RemoveVisualScenesLibrary ( unsigned int rIndex )
    {
        RSVisualScenesLibrary* library = GetVisualScenesLibrary ( rIndex );
        RSError error = RemoveVisualScenesLibrary ( library );
        return error;
    }

    RSError RSDocument::AddCamerasLibrary ( RSCamerasLibrary *rLibrary )
    {
        RSError error = AddChildElement ( rLibrary );
        return error;
    }

    RSError RSDocument::RemoveCamerasLibrary ( RSCamerasLibrary *rLibrary )
    {
        RSError error = RemoveChildElement ( rLibrary );
        return error;
    }

    RSError RSDocument::RemoveCamerasLibrary ( const QString& rId )
    {
        RSCamerasLibrary* library = GetCamerasLibrary ( rId );
        RSError error = RemoveCamerasLibrary ( library );
        return error;
    }

    RSError RSDocument::RemoveCamerasLibrary ( unsigned int rIndex )
    {
        RSCamerasLibrary* library = GetCamerasLibrary ( rIndex );
        RSError error = RemoveCamerasLibrary ( library );
        return error;
    }

    RSError RSDocument::SetScene ( RSScene* rScene )
    {
        RSError error = AddChildElement ( rScene );
        return error;
    }

    RSError RSDocument::BeforeAddChildElement ( RSElement* rChild )
    {
        RSError error = RSElement::BeforeAddChildElement ( rChild );

        if ( error == RS_INVALID_ELEMENT )
        {
            if ( rChild->InstanceOf ( RS_GEOMETRIES_LIBRARY_CLASS )
                    || rChild->InstanceOf ( RS_VISUAL_SCENES_LIBRARY_CLASS )
                    || rChild->InstanceOf ( RS_CAMERAS_LIBRARY_CLASS ) )
            {
                error = RS_NO_ERROR;
            }
            else if ( rChild->InstanceOf ( RS_SCENE_CLASS ) )
            {
                if ( mScene != NULL )
                {
                    RemoveChildElement ( mScene );
                    mScene = NULL;
                }

                error = RS_NO_ERROR;
            }
        }

        return error;
    }

    bool RSDocument::AfterAddChildElement ( RSElement* rChild )
    {
        bool handled = RSElement::AfterAddChildElement ( rChild );

        if ( !handled )
        {
            if ( rChild->InstanceOf ( RS_GEOMETRIES_LIBRARY_CLASS ) )
            {
                RSGeometriesLibrary* geometries_library = RSGeometriesLibraryCast ( rChild );
                mGeometriesLibraries.append ( geometries_library );
                handled = true;

                emit GeometriesLibraryAdded ( this, geometries_library );
            }
            else if ( rChild->InstanceOf ( RS_VISUAL_SCENES_LIBRARY_CLASS ) )
            {
                RSVisualScenesLibrary* visual_scene_library = RSVisualScenesLibraryCast ( rChild );
                mVisualScenesLibraries.append ( visual_scene_library );
                handled = true;

                emit VisualScenesLibraryAdded ( this, visual_scene_library );
            }
            if ( rChild->InstanceOf ( RS_CAMERAS_LIBRARY_CLASS ) )
            {
                RSCamerasLibrary* cameras_library = RSCamerasLibraryCast ( rChild );
                mCamerasLibraries.append ( cameras_library );
                handled = true;

                emit CamerasLibraryAdded ( this, cameras_library );
            }
            else if ( rChild->InstanceOf ( RS_SCENE_CLASS ) )
            {
                RSScene* scene = RSSceneCast ( rChild );
                mScene = scene;
                handled = true;

                emit SceneChanged ( this, scene );
            }
        }

        return handled;
    }

    bool RSDocument::AfterRemoveChildElement ( RSElement* rChild )
    {
        bool handled = RSElement::AfterAddChildElement ( rChild );

        if ( !handled )
        {
            if ( rChild->InstanceOf ( RS_GEOMETRIES_LIBRARY_CLASS ) )
            {
                RSGeometriesLibrary* geometries_library = RSGeometriesLibraryCast ( rChild );

                int index = mGeometriesLibraries.indexOf ( geometries_library );
                mGeometriesLibraries.removeAt ( index );
                handled = true;

                emit GeometriesLibraryRemoved ( this, geometries_library );
            }
            else if ( rChild->InstanceOf ( RS_VISUAL_SCENES_LIBRARY_CLASS ) )
            {
                RSVisualScenesLibrary* visual_scenes_library = RSVisualScenesLibraryCast ( rChild );
                int index = mVisualScenesLibraries.indexOf ( visual_scenes_library );
                mVisualScenesLibraries.removeAt ( index );
                handled = true;

                emit VisualScenesLibraryRemoved ( this, visual_scenes_library );
            }
            if ( rChild->InstanceOf ( RS_CAMERAS_LIBRARY_CLASS ) )
            {
                RSCamerasLibrary* cameras_library = RSCamerasLibraryCast ( rChild );

                int index = mCamerasLibraries.indexOf ( cameras_library );
                mCamerasLibraries.removeAt ( index );
                handled = true;

                emit CamerasLibraryRemoved ( this, cameras_library );
            }
            else if ( rChild->InstanceOf ( RS_SCENE_CLASS ) )
            {
                RSScene* scene = RSSceneCast ( rChild );
                mScene = NULL;
                handled = true;

                emit SceneChanged ( this, scene );
            }
        }

        return handled;
    }
}
